package com.aokang.footwear.common.utils;

import org.springframework.util.StringUtils;

import java.text.SimpleDateFormat;
import java.time.*;
import java.time.format.DateTimeFormatter;
import java.time.temporal.ChronoUnit;
import java.time.temporal.TemporalAdjusters;
import java.util.Date;

/**
 * @author chilei
 * @date 2022/11/24 16:07
 * 时间处理工具类
 */
public class MyDateUtil {

    public static final DateTimeFormatter Y_M_D = DateTimeFormatter.ofPattern("y-M-d");
    public static final DateTimeFormatter Y_M_D_H_M_S = DateTimeFormatter.ofPattern("y-M-d H:m:s");
    public static final DateTimeFormatter Y_MM_DD_HH_MM_SS = DateTimeFormatter.ofPattern("y-MM-dd HH:mm:ss");
    public static final DateTimeFormatter Y_MM_DD_HH_MM_SS_SSS = DateTimeFormatter.ofPattern("y-MM-dd HH:mm:ss.SSS");
    public static final DateTimeFormatter Y_M_D_T_H_M_S = DateTimeFormatter.ofPattern("y-M-d'T'H:m:s");
    public static final DateTimeFormatter Y_MM_DD_T_HH_MM_SS = DateTimeFormatter.ofPattern("y-MM-dd'T'HH:mm:ss");
    public static final DateTimeFormatter Y_M_D_T_H_M_S_8 = DateTimeFormatter.ofPattern("y-M-d'T'H:m:s+08:00");
    public static final DateTimeFormatter Y_MM_DD_T_HH_MM_SS_8 = DateTimeFormatter.ofPattern("y-MM-dd'T'HH:mm:ss+08:00");

    /**
     * 获取当地时区当时时间的偏移量
     *
     * @param localDateTime 当时时间
     * @param zone          时区
     * @return 偏移量 例：+13:00
     */
    public static ZoneOffset getZoneOffset(LocalDateTime localDateTime, String zone) {
        return ZoneOffset.of(ZoneId.of(zone).getRules().getOffset(localDateTime).getId());
    }

    /**
     * 获取当地时区当时时间的偏移量
     *
     * @param timestamp 当时时间戳
     * @param zone      时区
     * @return 偏移量 例：+13:00
     */
    public static ZoneOffset getZoneOffset(Long timestamp, String zone) {
        return ZoneOffset.of(ZoneId.of(zone).getRules().getOffset(Instant.ofEpochMilli(timestamp)).getId());
    }

    /**
     * LocalDateTime转时间戳
     *
     * @param localDateTime 时间
     * @param zoneOffset    偏移量
     * @return 时间戳
     */
    public static long toEpochMilli(LocalDateTime localDateTime, ZoneOffset zoneOffset) {
        return localDateTime.toInstant(zoneOffset).toEpochMilli();
    }

    /**
     * LocalDateTime转时间戳
     *
     * @param localDateTime 时间
     * @return 时间戳
     */
    public static long toEpochMilli(LocalDateTime localDateTime) {
        return localDateTime.toInstant(ZoneOffset.ofHours(8)).toEpochMilli();
    }

    /**
     * LocalDate转时间戳
     */
    public static long toEpochMilli(LocalDate localDate) {
        return localDate.atStartOfDay(ZoneId.systemDefault()).toInstant().toEpochMilli();
    }

    /**
     * LocalDate转时间戳
     *
     * @param localDate  当地时间
     * @param zoneOffset 时区偏移量
     * @return 时间戳
     */
    public static long toEpochMilli(LocalDate localDate, ZoneOffset zoneOffset) {
        return localDate.atStartOfDay(zoneOffset).toInstant().toEpochMilli();
    }

    /**
     * 时间戳转LocalDateTime
     *
     * @param timestamp 时间戳
     * @return 时间格式
     */
    public static LocalDateTime toLocalDateTime(Long timestamp) {
        return LocalDateTime.ofInstant(Instant.ofEpochMilli(timestamp), ZoneId.systemDefault());
    }

    /**
     * 时间戳转LocalDateTime
     *
     * @param timestamp 时间戳
     * @param zone      时区
     * @return 时间格式
     */
    public static LocalDateTime toLocalDateTime(Long timestamp, String zone) {
        return LocalDateTime.ofInstant(Instant.ofEpochMilli(timestamp), ZoneId.of(zone));
    }

    /**
     * 时间戳转LocalDateTime
     *
     * @param timestamp  时间戳
     * @param zoneOffset 时区偏移量
     * @return 时间格式
     */
    public static LocalDateTime toLocalDateTime(Long timestamp, ZoneOffset zoneOffset) {
        return LocalDateTime.ofInstant(Instant.ofEpochMilli(timestamp), zoneOffset);
    }

    /**
     * Date转LocalDateTime
     */
    public static LocalDateTime toLocalDateTime(Date date) {
        return date.toInstant().atZone(ZoneId.systemDefault()).toLocalDateTime();
    }

    /**
     * 字符串转时间格式
     *
     * @param dateStr 时间字符串
     */
    public static LocalDateTime toLocalDateTime(String dateStr) {
        return toLocalDateTime(dateStr, Y_M_D_H_M_S);
    }

    /**
     * 字符串转时间格式
     *
     * @param dateStr   时间字符串
     * @param formatter 格式化类型
     */
    public static LocalDateTime toLocalDateTime(String dateStr, DateTimeFormatter formatter) {
        if (StringUtils.hasText(dateStr)) {
            return LocalDateTime.parse(dateStr, formatter);
        }
        return null;
    }

    /**
     * LocalDateTime转时间字符串
     *
     * @param localDateTime 时间格式
     * @return 时间字符串
     */
    public static String formatLocalDateTime(LocalDateTime localDateTime) {
        return localDateTime.format(Y_MM_DD_HH_MM_SS);
    }

    /**
     * LocalDate转时间字符串
     *
     * @param localDate 时间格式
     * @return 字符串
     */
    public static String formatLocalDate(LocalDate localDate) {
        return localDate.format(Y_M_D);
    }

    /**
     * 字符串转时间格式
     *
     * @param dateStr 时间字符串
     */
    public static LocalDate toLocalDate(String dateStr) {
        return toLocalDate(dateStr, Y_M_D);
    }

    /**
     * 字符串转时间格式
     *
     * @param dateStr   时间字符串
     * @param formatter 格式化类型
     */
    public static LocalDate toLocalDate(String dateStr, DateTimeFormatter formatter) {
        if (StringUtils.hasText(dateStr)) {
            return LocalDate.parse(dateStr, formatter);
        }
        return null;
    }

    /**
     * 时间戳转LocalDate
     */
    public static LocalDate toLocalDate(Long timestamp) {
        return Instant.ofEpochMilli(timestamp).atZone(ZoneId.systemDefault()).toLocalDate();
    }

    /**
     * 时间戳转LocalDate
     *
     * @param timestamp 时间戳
     * @param zone      时区
     */
    public static LocalDate toLocalDate(Long timestamp, String zone) {
        return Instant.ofEpochMilli(timestamp).atZone(ZoneId.of(zone)).toLocalDate();
    }

    /**
     * 时间戳转LocalDate
     *
     * @param timestamp  时间戳
     * @param zoneOffset 时区偏移量
     * @return 时间格式
     */
    public static LocalDate toLocalDate(Long timestamp, ZoneOffset zoneOffset) {
        return Instant.ofEpochMilli(timestamp).atZone(zoneOffset).toLocalDate();
    }

    /**
     * Date转LocalDate
     */
    public static LocalDate toLocalDate(Date date) {
        return date.toInstant().atZone(ZoneId.systemDefault()).toLocalDate();
    }

    /**
     * LocalDate转Date
     */
    public static Date toDate(LocalDate localDate) {
        return Date.from(localDate.atStartOfDay(ZoneId.systemDefault()).toInstant());
    }

    /**
     * LocalDateTime转Date
     *
     * @param localDateTime java8时间
     * @return Date
     */
    public static Date toDate(LocalDateTime localDateTime) {
        return Date.from(localDateTime.atZone(ZoneId.systemDefault()).toInstant());
    }

    /**
     * Date转字符串
     *
     * @param date 时间
     * @return 字符串
     */
    public static String formatDate(Date date) {
        return new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(date);
    }

    /**
     * 月的第一天
     *
     * @param localDate 当地日期
     * @return {@link LocalDate}
     */
    public static LocalDate firstDay(LocalDate localDate) {
        return localDate.with(TemporalAdjusters.firstDayOfMonth());
    }

    /**
     * 月的最后一天
     *
     * @param localDate 当地日期
     * @return {@link LocalDate}
     */
    public static LocalDate lastDay(LocalDate localDate) {
        return localDate.with(TemporalAdjusters.lastDayOfMonth());
    }

    /**
     * 月的第一天(精确到秒)
     *
     * @param localDate 当地日期
     * @return {@link LocalDateTime}
     */
    public static LocalDateTime firstDateTime(LocalDate localDate) {
        return localDate.with(TemporalAdjusters.firstDayOfMonth()).atStartOfDay();
    }

    /**
     * 月的最后一天(精确到秒)
     *
     * @param localDate 当地日期
     * @return {@link LocalDateTime}
     */
    public static LocalDateTime lastDateTime(LocalDate localDate) {
        return localDate.with(TemporalAdjusters.lastDayOfMonth()).atTime(23, 59, 59);
    }

    /**
     * 两个时间相差天数（单位:天）
     *
     * @param from 开始时间
     * @param to   结束时间
     * @return 相差多少天
     */
    public static long differDay(LocalDate from, LocalDate to) {
        return from.until(to, ChronoUnit.DAYS);
    }

    /**
     * 两个时间相差天数（单位:天）
     *
     * @param from 开始时间
     * @param to   结束时间
     * @return 相差多少天
     */
    public static long differDay(LocalDateTime from, LocalDateTime to) {
        return from.until(to, ChronoUnit.DAYS);
    }

    /**
     * 两个时间相差时间（单位:秒）
     *
     * @param from 开始时间
     * @param to   结束时间
     * @return 相差多少秒
     */
    public static long differSeconds(LocalDateTime from, LocalDateTime to) {
        return from.until(to, ChronoUnit.SECONDS);
    }

    /**
     * 检查当前时间是否大于或等于给定时间。
     *
     * @param currentTime 当前时间
     * @param targetTime  给定时间
     * @return 如果当前时间大于或等于给定时间，则返回true，否则返回false
     */
    public static boolean isGE(LocalDateTime currentTime, LocalDateTime targetTime) {
        return !currentTime.isBefore(targetTime);
    }

    /**
     * 检查当前时间是否大于给定时间。
     *
     * @param currentTime 当前时间
     * @param targetTime  给定时间
     * @return 如果当前时间大于给定时间，则返回true，否则返回false
     */
    public static boolean isGT(LocalDateTime currentTime, LocalDateTime targetTime) {
        return currentTime.isAfter(targetTime);
    }

    /**
     * 检查当前时间是否小于或等于给定时间。
     *
     * @param currentTime 当前时间
     * @param targetTime  给定时间
     * @return 如果当前时间小于或等于给定时间，则返回true，否则返回false
     */
    public static boolean isLE(LocalDateTime currentTime, LocalDateTime targetTime) {
        return !currentTime.isAfter(targetTime);
    }

    /**
     * 检查当前时间是否小于给定时间。
     *
     * @param currentTime 当前时间
     * @param targetTime  给定时间
     * @return 如果当前时间小于给定时间，则返回true，否则返回false
     */
    public static boolean isLT(LocalDateTime currentTime, LocalDateTime targetTime) {
        return currentTime.isBefore(targetTime);
    }

    /**
     * 检查当前时间是否与给定时间相等。
     *
     * @param currentTime 当前时间
     * @param targetTime  给定时间
     * @return 如果当前时间与给定时间相等，则返回true，否则返回false
     */
    public static boolean isEqual(LocalDateTime currentTime, LocalDateTime targetTime) {
        return currentTime.isEqual(targetTime);
    }
}
